package com.hm.common.utils;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.util.DigestUtils;

import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Stream;

/**
 * redis工具类
 *
 * @author 020102
 * @date 2020-02-10 15:04
 */
@Repository
public class RedisUtils {
    private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create();

    @Autowired
    public RedisUtils(RedisTemplateOps redis) {
        RedisUtils.redisTemplateOps = redis;
    }

    private static RedisTemplateOps<String> redisTemplateOps;


    /**
     * 根据predicate判断是否永久存redis
     */
    public static <R> R getDataFromRedisOrNot(String key, TypeToken<R> resultType, Supplier<R> function, Predicate<R> predicate) {
        return getDataFromRedisOrNot(key, resultType, null, null, function, predicate);
    }

    /**
     * 根据predicate判断是否永久存redis
     */
    public static <R> R getDataFromRedisOrNot(String key, Class<R> resultClass, Supplier<R> function, Predicate<R> predicate) {
        return getDataFromRedisOrNot(key, resultClass, null, null, function, predicate);
    }

    /**
     * @param key        缓存的key
     * @param resultType 结果类型
     * @param timeout    过期时间
     * @param unit       过期时间单位
     * @param function   缓存中不存在时,获取结果的函数
     * @param predicate  根据function的结果判断是否要缓存,predicate 为null时会存缓存
     * @param <R>        结果泛型
     * @return 结果
     */
    public static <R> R getDataFromRedisOrNot(String key, TypeToken<R> resultType, Long timeout, TimeUnit unit, Supplier<R> function, Predicate<R> predicate) {
        Optional<String> resultOption = Optional.ofNullable(redisTemplateOps.get(key));
        if (resultOption.isPresent()) {
            return GSON.fromJson(resultOption.get(), resultType.getType());
        }
        return doDataFromRedisOrNot(key, timeout, unit, function, predicate);
    }

    /**
     * @param key         缓存的key
     * @param resultClass 结果类型
     * @param timeout     过期时间
     * @param unit        过期时间单位
     * @param function    缓存中不存在时,获取结果的函数
     * @param predicate   根据function的结果判断是否要缓存,predicate 为null时会存缓存
     * @param <R>         结果泛型
     * @return 结果
     */
    public static <R> R getDataFromRedisOrNot(String key, Class<R> resultClass, Long timeout, TimeUnit unit, Supplier<R> function, Predicate<R> predicate) {
        Optional<String> resultOption = Optional.ofNullable(redisTemplateOps.get(key));
        if (resultOption.isPresent()) {
            return GSON.fromJson(resultOption.get(), resultClass);
        }
        return doDataFromRedisOrNot(key, timeout, unit, function, predicate);
    }

    /**
     * 执行function和predicate 获取结果 并根据条件缓存到redis
     *
     * @param key       缓存的key
     * @param timeout   过期时间,timeout 为 null 默认缓存 1 天
     * @param unit      过期时间单位
     * @param function  缓存中不存在时,获取结果的函数
     * @param predicate 根据function的结果判断是否要缓存,predicate 为null时会存缓存
     * @param <R>       结果泛型
     * @return 结果
     */
    private static <R> R doDataFromRedisOrNot(String key, Long timeout, TimeUnit unit, Supplier<R> function, Predicate<R> predicate) {
        R apply = function.get();
        if (predicate == null || predicate.test(apply)) {
            if (timeout == null || unit == null) {
                redisTemplateOps.set(key, GSON.toJson(apply), 1L, TimeUnit.DAYS);
            } else {
                redisTemplateOps.set(key, GSON.toJson(apply), timeout, unit);
            }
        }
        return apply;
    }

    /**
     * 获取redisKey
     *
     * @param source 参数
     * @param group  分组(redis默认以冒号(:)分组)
     * @return
     */
    public static String getRedisKey(Object source, String... group) {
        String result = Stream.of(group).map(k -> k + ":").reduce((k, v) -> k + v).orElse("default:");
        String sourceString = DigestUtils.md5DigestAsHex(GSON.toJson(source).getBytes());
        return result.concat(sourceString);
    }


    public static void main(String[] args) {
        Stream.of("1", "2", "3", "4", "5", "6").reduce((k, v) -> k + v).orElse("default");
    }
}
